﻿using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;
using MyBasicLib.Extensions;
using System;
using System.Linq;
using MyBasicLib.Utilities;

namespace MyBasicLib.Data.ORMLiteNew
{
 
    public abstract class DbQueryProvider : IDbQueryProvider
    {
       //public Database Db = Utilities.IOC.DefaultContainer.Resolve<IDatabase>() as Database;
            protected IDatabase Db { get; set; }
        ILogger logger;
        public ILogger Logger
        {
            get { return logger ?? (logger = (Db == null || Db.Logger == null) ? Utilities.IOC.DefaultContainer.Resolve<ILogger>() : Db.Logger); }
            set { logger = value; }
        }
        public string ConnectionString
        {
            get
            {
                if (Db == null) return null;
                return Db.ConnectionString;
            }
        }
        //private readonly DataContext dataContext;

        //internal DbQueryProvider(DataContext dc)
        //{
        //    this.dataContext = dc;
        //}

        //DataContext IDbQueryProvider.DataContext
        //{
        //    get { return dataContext; }
        //}

        #region IDbQueryProvider Members


        #endregion

        protected virtual string GenerateSql(IDbQueryable queryable, out IList<object> parameterValues, out CommandType commandType)
        {
            commandType = CommandType.Text;
            DbQueryInfo queryInfo = DbQueryInfo.Parse(queryable.DbExpressions);

            if (queryInfo.InsertExpression != null)
            {
                return HandleInsert(queryInfo, out parameterValues);
            }
            if (queryInfo.UpdateExpression != null)
            {
                return HandleUpdate(queryInfo, out parameterValues);
            }
            if (queryInfo.DeleteExpression != null)
            {
                return HandleDelete(queryInfo, out parameterValues);
            }

            return HandleSelect(queryInfo, out parameterValues);
        }

        protected abstract string HandleSelect(DbQueryInfo queryInfo, out IList<object> parameterValues);

        protected abstract string HandleDelete(DbQueryInfo queryInfo, out IList<object> parameterValues);

        protected abstract string HandleUpdate(DbQueryInfo queryInfo, out IList<object> parameterValues);

        protected abstract string HandleInsert(DbQueryInfo queryInfo, out IList<object> parameterValues);


        protected static Expression MergeWhereExpressions(Queue<Expression> queue)
        {
            Expression exp = null;
        Line1:
            if (queue.Count > 0)
            {
                exp = queue.Dequeue();
                if (exp == null)
                {
                    goto Line1;
                }
                exp = ((LambdaExpression)exp.TrimUnary()).Body;

                while (queue.Count > 0)
                {
                    Expression nExp = queue.Dequeue();
                    if (nExp == null) continue;
                    exp = Expression.And(exp, ((LambdaExpression)nExp.TrimUnary()).Body);
                }
            }
            return exp;
        }

        public IList<TElement> ExecuteList<TElement>(IDbQueryable queryable)
        {
            var cmd = CreateCommand(queryable);
            return Db.ExecuteList<TElement>(null, cmd).ToArray();
        }

        public TElement Execute<TElement>(IDbQueryable queryable)
        {
            var cmd = CreateCommand(queryable);
            return Db.Execute<TElement>(null, cmd);
        }

        public int SubmitChanges(IList<IDbQueryable> queryable)
        {
            int affected = 0;
            if (queryable.Count < 1) return 0;
            lock (queryable)
            {
                if (queryable.Count < 1) return 0; 
                var list = new List<IDbCommand>(queryable.Count);
                list.AddRange(queryable.Select(p => CreateCommand(p)));


                using (IDbConnection conn = Db.CreateNewOpenConnection())
                {
                    if (conn == null) return 0;
                    using (IDbTransaction trans = conn.BeginTransaction())
                    {
                        try
                        {
                            foreach (IDbCommand cmd in list)
                            {
                                affected += Db.ExecuteNonQuery(trans, cmd);
                            }
                            trans.Commit();
                            queryable.Clear();
                        }
                        catch (Exception ex)
                        {
                            trans.Rollback();
                            Logger.Error(ex.ToString());
                            //Db.Logger.Error(ex.ToString());
                            throw;
                        }
                    }
                }
                return affected;
            }
        }


        public IDbCommand CreateCommand(IDbQueryable queryable)
        {
            //try
            {
                IList<object> parameterValues;
                CommandType commandType;
                string sql = GenerateSql(queryable, out parameterValues, out commandType);
                //log.Info(sql); 
                IDbCommand cmd = Db.CreateCommand(sql);
                //cmd.CommandText = sql;
                BuildParameters(cmd, parameterValues);
                return cmd;
            }
           // catch (Exception ex)
            {
               // Logger.Error(ex.ToString());
                //Db.Logger.Error(ex.ToString());
              //  throw;
            }
        }
        string[]
            _parameterNames, _parameterNamesWithToken;
        public string[] GetParameterNames()
        {
            if (_parameterNames == null)
            {
                var parameterNames1 = new string[2010]; //2100
                for (int i = 0; i < parameterNames1.Length; i++)
                {
                    parameterNames1[i] = "p" + i;
                }
                _parameterNames = parameterNames1;
            }
            return _parameterNames;
        }

        public string[] GetParameterNamesWithToken()
        {
            if (_parameterNamesWithToken == null)
            {
                _parameterNamesWithToken = GetParameterNames().Select(p => "@" + p).ToArray();
            }
            return _parameterNamesWithToken;
        }
        private   void BuildParameters(IDbCommand cmd, IEnumerable<object> values)
        {
            int pCount = 0;
            string[] ps =  GetParameterNames();
            foreach (object parameterValue in values)
            {
                IDbDataParameter p0 = cmd.CreateParameter(); 
                p0.ParameterName = ps[pCount++];
                p0.Value = parameterValue ?? DBNull.Value;
                cmd.Parameters.Add(p0);
            }
        }
    }
}